package cn.edu.xmu.datamining.tangzk.mralgos.sssp;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.hadoop.io.Writable;

import cn.edu.xmu.datamining.tangzk.util.MyUtil;

import com.google.common.base.Objects;

public class GraphNodeWritable implements Writable {

	private boolean isNode = false;
	private int distance;
	private List<GraphEdge> edgeList = null;

	public GraphNodeWritable() {
	}

	public GraphNodeWritable(int distance, String adjString) {
		this.isNode = true;
		this.distance = distance;
		if (!"".equals(adjString)) {
			String[] flds = adjString.split(",");
			String[] vals = null;
			edgeList = new ArrayList<GraphEdge>();
			int id, weight;
			for (String fld : flds) {
				vals = fld.split(":");
				id = Integer.parseInt(vals[0]);
				weight = MyUtil.parseInt(vals[1]);
				edgeList.add(new GraphEdge(id, weight));
			}
		}
	}

	public GraphNodeWritable(int distance) {
		this.isNode = false;
		this.distance = distance;
	}

	@Override
	public void readFields(DataInput in) throws IOException {
		this.isNode = in.readBoolean();
		this.distance = in.readInt();
		if (this.isNode) {
			int l = in.readInt();
			if (l > 0) {
				this.edgeList = new ArrayList<GraphEdge>(l);
				GraphEdge edge = null;
				for (int i = 0; i < l; i++) {
					edge = new GraphEdge();
					edge.readFields(in);
					this.edgeList.add(edge);
				}
			}
		}
	}

	@Override
	public void write(DataOutput out) throws IOException {
		out.writeBoolean(isNode);
		out.writeInt(this.distance);
		if (this.isNode) {
			if (this.edgeList != null) {
				out.writeInt(this.edgeList.size());
				for (GraphEdge edge : this.edgeList) {
					edge.write(out);
				}
			} else {
				out.writeInt(0);
			}
		}
	}

	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof GraphNodeWritable)) {
			return false;
		}
		GraphNodeWritable node = (GraphNodeWritable) obj;
		boolean flag = false;
		if (this.isNode != node.isNode || this.distance != node.distance) {
			return false;
		} else {
			if (this.isNode && this.edgeList != null) {
				int l1 = this.edgeList.size();
				int l2 = node.edgeList.size();
				if (l1 != l2) {
					return false;
				}
				for (int i = 0; i < l1; i++) {
					flag = this.edgeList.get(i).equals(node.edgeList.get(i));
					if (!flag) {
						return false;
					}
				}
			}
		}
		return true;
	}

	@Override
	public int hashCode() {
		int code = this.distance;
		if (this.isNode && this.edgeList != null) {
			for (GraphEdge edge : this.edgeList) {
				code = code * 17 + edge.hashCode();
			}
		}
		return code;
	}

	@Override
	protected GraphNodeWritable clone() {
		GraphNodeWritable node = new GraphNodeWritable();
		node.isNode = this.isNode;
		node.distance = this.distance;
		if (this.isNode && this.edgeList != null) {
			node.edgeList = new ArrayList<GraphEdge>(this.edgeList.size());
			for (GraphEdge edge : this.edgeList) {
				node.edgeList.add(edge.clone());
			}
		}
		return node;
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append(this.isNode ? 0 : 1);
		sb.append(",");
		sb.append(MyUtil.formatInt(this.distance));
		if (this.isNode && this.edgeList != null) {
			sb.append(",");
			sb.append(this.edgeList.toString());
		}
		return sb.toString();
	}

	public boolean isNode() {
		return isNode;
	}

	public void setNode(boolean isNode) {
		this.isNode = isNode;
	}

	public int getDistance() {
		return distance;
	}

	public void setDistance(int distance) {
		this.distance = distance;
	}

	public List<GraphEdge> getEdgeList() {
		return edgeList;
	}

	public void setEdgeList(List<GraphEdge> edgeList) {
		this.edgeList = edgeList;
	}

}
